# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# 
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# Author: Michael DiBernardo (mikedebo@gmail.com)
# Copyright Michael DiBernardo 2009
"""
Views for talking to a ping ticker.
"""
from tikk.core.models import *
import tikk.core.injectibles as injectibles
import tikk.core.view_responses as view_responses

def handle_ping(request, ticker_name, identity):
    """
    Handle a ping request by creating a new tick for the given identity.
    """
    factory_fcn = lambda ticker: Tick(ticker=ticker, timestamp=injectibles.timestamp(), identity=identity)
    return _handler_template(ticker_name, factory_fcn)

def handle_counter(request, ticker_name, identity, value):
    """
    Handle a counter tick with the given value.
    """
    try:
        float(value)
    except ValueError: 
        return view_responses.not_allowed()

    factory_fcn = lambda ticker: CounterTick(ticker=ticker, timestamp=injectibles.timestamp(), identity=identity, value=value)
    return _handler_template(ticker_name, factory_fcn)

def handle_duration(request, ticker_name, identity, startstop_tag):
    """
    Handle a counter tick with the given value.
    """
    if startstop_tag not in ("begin", "end"):
        return view_responses.not_allowed()

    try:
        # Try to create the start/stop event as requested.
        if startstop_tag == "begin":
            # Use a vanilla tick.
            return _handler_template(ticker_name, StartTickFactoryFcn(identity))
        else:
            return _handler_template(ticker_name, EndTickFactoryFcn(identity))
    except DurationEventSequenceError:
        # If the user has caused a sequencing error, return a 403.
        return view_responses.not_allowed()

class StartTickFactoryFcn(object):
    """
    Factory function for start ticks. Because it's a bit more involved to do this,
    it is seperated out as a bonafide function instead of being shoved in a
    lambda.
    """
    def __init__(self, identity):
        self.identity = identity

    def __call__(self, ticker):
        # The tick that we will return if everything goes well.
        new_tick = Tick(ticker=ticker, timestamp=injectibles.timestamp(),
                identity=self.identity)

        # Look up the most recent event and make sure it's not a start event.
        try:
            last_tick = get_most_recent_tick_for(ticker, self.identity)
        except:
            # There was no last tick: We're OK.
            return new_tick

        # If we had a tick, it might be start tick as well, and we don't allow
        # two of these in a row.
        try:
            last_tick.endtick
            return new_tick
        except:
            raise DurationEventSequenceError("Two start events in a row.")

class EndTickFactoryFcn(object):
    """
    Factory function for endticks. 
    """
    def __init__(self, identity):
        self.identity = identity

    def __call__(self, ticker):
        try:
            last_tick = get_most_recent_tick_for(ticker, self.identity)
        except:
            raise DurationEventSequenceError("Used end event before start event.")

        try:
            last_tick.endtick
        except Exception, e:
            return EndTick.createFromStartTick(last_tick, injectibles.timestamp())
        else:
            raise DurationEventSequenceError("Used end event before start event.")

class DurationEventSequenceError(Exception):
    """
    Exception thrown when an invalid sequence of events (two starts, two
    stops, or stop before start) occurs on a duration ticker for a
    particular id.
    """
    pass

def _handler_template(ticker_name, factory_fcn):
    """
    Template function that does error checking on tick creation and creates the
    tick using a factory function if everything is cool.
    """
    try:
        ticker = Ticker.objects.get(name__exact=ticker_name)
    except Ticker.DoesNotExist:
        return view_responses.no_such_ticker(ticker_name)

    new_tick = factory_fcn(ticker)
    new_tick.save()

    return view_responses.everything_is_cool()
